#include "StdAfx.h"
#include <string>
#include "RibbonCompiler.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include "Util.h"

using namespace std;


CRibbonCompiler::CRibbonCompiler(const wstring &ribbonXMLFile, LPCTSTR szUICCPath, LPCTSTR szRCPath, LPCTSTR szLINKPath) 
	: m_sRibbonXMLFile(ribbonXMLFile),
	m_sUICCPath(szUICCPath),
	m_sRCPath(szRCPath),
	m_sLINKPath(szLINKPath),
	m_bSuccessBuild(false)
{
	::GetTempPath(MAX_PATH, m_sTempPath);
}

CRibbonCompiler::~CRibbonCompiler(void)
{
}

DWORD CRibbonCompiler::ExecuteExternalTool(const wstring &commandLine)
{
	HANDLE hReadPipe, hWritePipe;
	PROCESS_INFORMATION processInformation;
	STARTUPINFO startupInfo;

	SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
	

	if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0) == FALSE)
		return ::GetLastError();

	//Get the current process startup structure.
	GetStartupInfo(&startupInfo);
	//set the stderr handle.
	startupInfo.hStdError = hWritePipe;
	startupInfo.hStdOutput = hWritePipe;
	startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
	//tell CreateProcess to use the standard handles.
	startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
	startupInfo.wShowWindow = SW_HIDE;

	//TODO: change to sdk path from environment variable and the temp from get temporary folder
	
	m_sOutput += L"\r\n\r\nExecuting: ";
	m_sOutput += commandLine;
	m_sOutput += L"\r\n\r\n";

	if (!CreateProcess(0, const_cast<LPWSTR>(commandLine.c_str()), 0, 0, TRUE, 0, 0, 0, &startupInfo, &processInformation))
	{
		m_sOutput += L"CreateProcess failed.\r\n\r\n";
		return GetLastError();
	}

	
	CloseHandle(processInformation.hThread);
	CloseHandle(hWritePipe);


	char buffer[4096];
	DWORD numberRead;
	string collector;

	while (ReadFile(hReadPipe, (LPVOID)buffer, 4096, &numberRead, 0))
	{
		string str(buffer, numberRead);
		collector += str;
	}

	wstring wstr(collector.length(), 0 );
	MultiByteToWideChar( CP_ACP, 0,	collector.c_str(), (int)collector.length(), &wstr[0], (int)collector.length() );
	
	m_sOutput += wstr;


	DWORD compilationResult;
	if (GetExitCodeProcess(processInformation.hProcess, &compilationResult) == FALSE)
	{
		compilationResult = GetLastError();
	}

	CloseHandle(hReadPipe);
	CloseHandle(processInformation.hProcess);

	return compilationResult;
			
}


DWORD CRibbonCompiler::UICC()
{
	wstring command = L"\"";
	command.append(m_sUICCPath);
	command.append(L"\" \"");
	command.append(m_sRibbonXMLFile);
	command.append(L"\" \"");
	command.append(m_sTempPath);
	command.append(L"Ribbon.bin\" /header:\"");
	command.append(m_sTempPath);
	command.append(L"Ribbon.h\" /res:\"");
	command.append(m_sTempPath);
	command.append(L"Ribbon.rc\"");

	return ExecuteExternalTool(command);
}

DWORD CRibbonCompiler::RC()
{
	wstring command = L"\"";
	command.append(m_sRCPath);
	command.append(L"\" /v ");
	wstring sourceDirectory = GetDirectoryPath(m_sRibbonXMLFile);
	if (!sourceDirectory.empty())
	{
		command.append(L" /i \"");
		command.append(sourceDirectory);
		command.append(L"\" \"");
	}

	command.append(m_sTempPath);
	command.append(L"Ribbon.rc\"");
	
	return ExecuteExternalTool(command);
}

DWORD CRibbonCompiler::LINK(const wstring &outputDLLName)
{
	wstring command = L"\"";
	command.append(m_sLINKPath);
	command.append(L"\" /VERBOSE /NOENTRY /DLL /OUT:\"");
	command.append(outputDLLName);
	command.append(L"\" \"");
	command.append(m_sTempPath);
	command.append(L"Ribbon.res\"");
	
	
	return ExecuteExternalTool(command);
}


DWORD CRibbonCompiler::Compile(const wstring & outputDLLName)
{
	DWORD result;

	ReadSourceXMLFile();

	result = UICC();
	if (result != 0)
		return result;

	ReadHeaderFile();

	result = RC();
	if (result != 0)
		return result;

	ReadResourceFile();

	result = LINK(outputDLLName);
	
	m_bSuccessBuild = result == 0;

	return result;
}

void CRibbonCompiler::ReadHeaderFile()
{
	wstring sHeaderFileName(m_sTempPath);
	sHeaderFileName.append(L"Ribbon.h");

	m_sHeaderFile = GetFileText(sHeaderFileName);
}

void CRibbonCompiler::ReadSourceXMLFile()
{
	m_sSourceXMLFileText = GetFileText(m_sRibbonXMLFile);
}


void CRibbonCompiler::ReadResourceFile()
{
	wstring sResourceFileName(m_sTempPath);
	sResourceFileName.append(L"Ribbon.rc");

	m_sResourceFile = GetFileText(sResourceFileName);
}


const wstring &CRibbonCompiler::GetOutput() const
{
	return m_sOutput;
}

wstring CRibbonCompiler::GetFileText(const wstring &filePath) const
{
	return CUtil::ReadFileText(filePath);
}


wstring CRibbonCompiler::GetHeaderFileText() const
{
	if (!m_bSuccessBuild)
		return L"";

	return m_sHeaderFile;
}

wstring CRibbonCompiler::GetResourceFileText() const
{
	if (!m_bSuccessBuild)
		return L"";

	return m_sResourceFile;
}

wstring CRibbonCompiler::GetSourceXMLFileText() const
{
	if (!m_bSuccessBuild)
		return L"";

	return m_sSourceXMLFileText;
}


wstring CRibbonCompiler::GetDirectoryPath(const wstring &filePath) const
{
	wstring::size_type lastBackslash = filePath.find_last_of(L'\\');
	if (lastBackslash == wstring::npos)
		return wstring();

	return filePath.substr(0, lastBackslash); 
}